<template>
  <div class="customToolbarContainer">
    <!-- 左侧节点/放大缩小工具 菜单 -->
    <div class="toolbarContainer" style="padding-left:10px">
      <!-- 节点样板菜单 -->
        <div v-for="item in generalToolbarItems" :key="item['index']" :style="item['style']" :class="item['class']"  ref="generalToolItems" class="toolBox">
            <span class="generalTooltitle">{{ item["text"] }}</span>
        </div>
          <ul>
            <li
              v-for="(item, index) in grouptoolItems"
              :key="index"
              class="group-item"
              ref="grouptoolItem"
            >
              <b>{{ item.title }}</b>
            </li>
          </ul>
    </div>
    <!-- 画布的顶部工具栏 -->
    <div class="top-tools">
      <el-col :span="12" class="tools-group" v-show="isShowGraph">
        <el-tooltip class="item" effect="dark" content="导入xml文件" placement="bottom">
          <el-button type="text" icon="iconfont icon-fileplus-fill" @click="inPutXml"></el-button>
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="导出xml文件" placement="bottom">
          <el-button type="text" icon="iconfont icon-xmloutput" @click="outPutXml"></el-button>
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="保存" placement="bottom">
          <el-button type="text" icon="iconfont icon-xmloutput" @click="handleSaveXml"></el-button>
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="撤销" placement="bottom">
          <el-button type="text" icon="iconfont icon-reply" @click="goBack"></el-button>
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="前进" placement="bottom">
          <el-button type="text" icon="iconfont icon-share" @click="goForward"></el-button>
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="删除" placement="bottom">
          <el-button type="text" icon="el-icon-delete-solid" @click="deleteNode"></el-button>
        </el-tooltip>

        <el-tooltip class="item" effect="dark" content="放大" placement="bottom">
          <el-button type="text" icon="el-icon-zoom-in" @click="zoomIn"></el-button>
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="缩小" placement="bottom">
          <el-button type="text" icon="el-icon-zoom-out" @click="zoomOut"></el-button>
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="等比缩放" placement="bottom">
          <el-button @click="autoSize" type="text" icon="iconfont icon-expandalt-fill"></el-button>
        </el-tooltip>
      </el-col>
       <el-button round size="medium" @click="loadFlowCase(1)"
            >化工流程案例</el-button
          > 
    </div>
    <!-- 中心画布 -->
    <div v-show="isShowGraph" class="graphContainer" id="graphContainer" ref="container"
      :class="{ 'graphContainer-background': showBackground }"></div>
    <!-- 右侧栏 -->
    <div class="right-bar" v-show="isShowGraph">

      <style-select @changeDashed="changeDashed" @changeStrokeColor="changeStrokeColor"
        @changeStrokeWidth="changeStrokeWidth" @changeFontSize="changeFontSize" @changeFontColor="changeFontColor"
        @changeLabelBackgroundColor="changeLabelBackgroundColor" @changeConfigOrder="changeConfigOrder"
        @changeFillColor="changeFillColor" @changeShadow="changeShadow" @changeFontStyle="changeFontStyle"
        @changeNodeimage="changeNodeimage" @edgeChange="edgeChange" @textValueChange="textValueChange"
        :textValue="textValue" :isNode="isNode" :cellStyle="cellStyle" :currentNormalType="currentNormalType"
        :graphX="graphX" :graphY="graphY" ref="styleSelect" />
      <!-- <div class="json-viewer">
        <h4 style="text-align: center">Json数据结构</h4>
        <json-viewer
          :value="jsonData"
          style="height: 80%"
          copyable
          sort
        ></json-viewer>
      </div> -->
    </div>

    <!-- XML数据导入/导出 -->
    <upload-data v-if="uploadDataVisible" @uploadPaintFlow="uploadPaintFlow" :graphXml="graphXml"
      :isOutputXml="isOutputXml"></upload-data>
  </div>
</template>
<script>
// 导入自定义图标数组
import { generalToolbarItems } from "./general-shape"
import { toolbarItems } from './toolbar'
import uploadData from "./component/uploadData"
// 组元素
import { grouptoolItems } from './GroupToolbarItems'
import * as R from "ramda";
import mx from 'mxgraph';
import _ from 'lodash';
import {addFormula} from "@/api/addFormula"
const mxgraph = mx({})
import styleSelect from "./component/styleSelect";
// import { parseXMLToAdjacencyList, isConnected } from "@/utlis/xmlTransformJson.js"
import { formula } from "@/api/formula"
const {
  mxStencilRegistry,
  mxStencil,
  mxEvent,
  mxGraph,
  mxEditor,
  mxUtils,
  mxRubberband,
  mxKeyHandler,
  mxGraphHandler,
  mxConstants,
  mxImage,
  mxCellState,
  mxConnectionHandler,
  mxCodec,
  mxRectangleShape,
  mxPoint,
  mxClipboard,
  mxCellTracker,
  mxUndoManager,
  mxClient,
  mxEdgeHandler,
  mxPerimeter,
  mxOutline,
  mxEventObject,
  mxGeometry,
  mxCell,
  mxShape,
  mxConstraintHandler,
  mxEllipse,
  // mxTriangle,
  mxFastOrganicLayout,
  mxHierarchicalLayout,
  mxCompactTreeLayout,
  mxMorphing,
  mxCircleLayout,
  mxSvgCanvas2D,
  mxImageExport,
  mxConnectionConstraint,
  mxPolyline,
  mxVertexHandler,
  mxRectangle,
  mxImageShape,
  ActiveXObject,
} = mxgraph;
const path = require('path');
// 配置自定义事件
Object.assign(mxEvent, {
  NORMAL_TYPE_CLICKED: 'NORMAL_TYPE_CLICKED',
});
// 导入流程图案例数据
const xmlData1 = path.join('data/data1.xml');
const xmlData2 = path.join('data/data2.xml');
const xmlData3 = path.join('data/data3.xml');


export default {
  props: {
    recipeId: String,
    currObjMx: Object,
    count: Number,
    widthVal: String,
    trainId:String,
    formulaCount:Number
  },
  computed: {
    // 组元素
    grouptoolItems: () => grouptoolItems,
    toolbarItems: () => toolbarItems,
    generalToolbarItems: () => generalToolbarItems,
  },
  components: {
    styleSelect,
    uploadData,
  },
  data() {
    return {
      graph: null,
      editor: null,
      palettes: {},
      graphXml: '',
      activeNames: ['1', '2', '3'],
      isNode: false,
      cellStyle: '',
      graphX: 100,
      graphY: 10,
      undoMng: '',
      textValue: '',
      uploadDataVisible: false,
      isOutputXml: false,
      edgeStyle: 'orthogonalEdgeStyle',
      outline: '',
      idSeed: 3,
      normalIdSeed: 0,
      // configOrder: 0,
      jsonData: {
        cells: {
          nodes: [],
          groups: []
        },
        edges: []
      },
      showBackground: true,
      currentNormalType: {},
      normalTypePosition: {
        top: '0',
        left: '0',
      },
      isConnection: false,
      isTwoClick: null,
      selectedNode: {},
      isShowGraph: true,
      clickTimeout:null,
      disStart:false,
      disEnd:false,
    };
  },
  watch: {
    count(val) {
      if (val) {
        this.$nextTick(() => {
          if (this.selectedNode) {
            // this.selectedNode.currObjMx = JSON.stringify(this.currObjMx)
            console.log(this.selectedNode)
            this.selectedNode.equipClsId = this.currObjMx.initSchedEntId //设备类型id
            this.selectedNode.equipId = this.currObjMx.equipmentId //设备Id
            this.graph.model.setValue(this.selectedNode, this.currObjMx.jobDesc);
            console.log("按钮已设置文字到节点:", this.selectedNode.value);
          } else {
            console.warn("请先选中一个节点！");
          }
        })

      }
    },
    formulaCount(val){
      if(val){
        this.findIconData()
      }
     
      // this.loadGraphFromXml(this.graph,val)
    }
  },
  methods: {
    // 选择配方接口
    findIconData(){
      addFormula.findIconDataApi({recipeId:this.recipeId})
      .then(res =>{
        if(res.code == 200){
           this.graph.selectAll();
          this.graph.removeCells(this.graph.getSelectionCells());
          setTimeout(() => {
            this.decode(res.data.graphContent, this.graph);
          }, 1000);
        }else{
          this.$message.error(res.msg)
        }
      })
      .catch(error =>{
        console.log(error)
      })
    },
    // 创建画布并进行初始化
    createGraph() {
      // 创建graph
      // 方式一：直接构建graph实例
      // this.graph = new mxGraph(this.$refs.container)
      this.editor = new mxEditor();
      this.graph = this.editor.graph;
      this.editor.setGraphContainer(this.$refs.container);
      // 配置默认全局样式
      this.configureStylesheet(this.graph);

      let defaultEdgeStyle = this.graph.getStylesheet().getDefaultEdgeStyle();
      defaultEdgeStyle[mxConstants.STYLE_STROKECOLOR] = '#333';
      defaultEdgeStyle[mxConstants.STYLE_STROKEWIDTH] = 1; 

      // 去锯齿效果
      mxRectangleShape.prototype.crisp = true;
      // 定义全局变量，如。用于触发建立新的连接的活动区域的最小尺寸（以像素为单位），该部分（100％）的小区区域被用于触发新的连接，以及一些窗口和“下拉菜菜单选择
      mxConstants.MIN_HOTSPOT_SIZE = 16;
      mxConstants.DEFAULT_HOTSPOT = 1;

      //cell创建支持传入html
      this.graph.setHtmlLabels(true);
      this.graph.setDropEnabled(true);
      this.graph.setSplitEnabled(false);
      // 有效的拖放操作，则返回true
      this.graph.isValidDropTarget = (target, cells, evt) => {
        if (this.graph.isSplitEnabled() && this.graph.isSplitTarget(target, cells, evt)) {
          console.log('拖放');
          return true;
        }
      };

      // 禁用分组的收缩功能 方法1:
      // this.graph.isCellFoldable = (cell) => {
      //   return false
      // }
      // 禁用分组的收缩功能 方法2:
      this.graph.foldingEnabled = false;
      // 组内的子元素是否随父元素变化而变化
      this.graph.recursiveResize = true;

      // 设置连线时的预览路径及样式
      this.graph.connectionHandler.createEdgeState = () => {
        // 设置预览的连接线,第三个参数为连接成功后连接线上的label
        var edge = this.graph.createEdge(null, null, null, null, null);
        // edge.style += `;edgeStyle=orthogonalEdgeStyle `
        return new mxCellState(this.graph.view, edge, this.graph.getCellStyle(edge));
      };

      // 是否开启旋转
      mxVertexHandler.prototype.livePreview = true;
      mxVertexHandler.prototype.rotationEnabled = true;
      // 设置旋转按钮
      mxVertexHandler.prototype.createSizerShape = function (bounds, index, fillColor) {
        if (this.handleImage != null) {
          bounds = new mxRectangle(bounds.x, bounds.y, this.handleImage.width, this.handleImage.height);
          let shape = new mxImageShape(bounds, this.handleImage.src);
          // Allows HTML rendering of the images
          shape.preserveImageAspect = true;
          return shape;
        } else if (index == mxEvent.ROTATION_HANDLE) {
          // return new mxDoubleEllipse(bounds, fillColor || mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
          // 设置旋转图标
          bounds = new mxRectangle(bounds.x, bounds.y, 15, 15);
          let rotationShape = new mxImageShape(bounds, 'icon/rotate.svg');
          rotationShape.preserveImageAspect = true;
          return rotationShape;
        } else {
          return new mxRectangleShape(bounds, fillColor || mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR);
        }
      };
      // 设置旋转角度（解决默认旋转180度的bug）
      mxVertexHandler.prototype.getRotationHandlePosition = function () {
        let padding = this.getHandlePadding();
        return new mxPoint(this.bounds.x + this.bounds.width - this.rotationHandleVSpacing + padding.x / 2, this.bounds.y + this.rotationHandleVSpacing - padding.y / 2);
      };
      // 设置默认组
      // groupBorderSize 设置图形和它的子元素的边距
      let group = new mxCell('Group', new mxGeometry(), 'group;fontColor=white;');
      group.setVertex(true);
      // 设置组可连接
      group.setConnectable(true);
      // group.setCellsResizable(false);
      this.editor.defaultGroup = group;
      this.editor.groupBorderSize = 80;

      // 是否根元素
      this.graph.isValidRoot = function (cell) {
        return this.isValidDropTarget(cell);
      };

      // // 是否可以被选中
      // this.graph.isCellSelectable = function (cell) {
      //   return !this.isCellLocked(cell);
      // };

      // 返回元素
      this.graph.getLabel = function (cell) {
        var tmp = mxGraph.prototype.getLabel.apply(this, arguments); // "supercall"
        if (this.isCellLocked(cell)) {
          // 如元素被锁定 返回空标签
          return '';
        } else if (this.isCellCollapsed(cell)) {
          var index = tmp.indexOf('</h1>');
          if (index > 0) {
            tmp = tmp.substring(0, index + 5);
          }
        }
        return tmp;
      };

      // 目标是否有效
      this.graph.isValidDropTarget = function (cell) {
        // console.log(cell, cells, evt);
        return this.isSwimlane(cell);
      };

      // 是否根元素
      this.graph.isValidRoot = function (cell) {
        return this.isValidDropTarget(cell);
      };

      // 是否可以被选中
      this.graph.isCellSelectable(true)

      // 允许重复连接
      this.graph.setMultigraph(true);
      // 禁止连接线晃动(即连线两端必须在节点上)
      this.graph.setAllowDanglingEdges(false);
      // 允许连线的目标和源是同一元素
      this.graph.setAllowLoops(false);
      //边被拖动时始终保持连接
      this.graph.setDisconnectOnMove(false);
      // 选择基本元素开启  --
      this.graph.setEnabled(true);
      // 动态改变样式
      this.graph.getView().updateStyle = true;
      // 鼠标框选
      this.rubberBand = new mxRubberband(this.graph);
      this.graph.setResizeContainer(true);

      // 开启画布平滑移动
      // this.graph.setPanning(true);
      this.graph.setPanning = true;
      // 开启提示
      this.graph.setTooltips(false);
      // 允许连线
      this.graph.setConnectable(true);
      //移动元素的步长
      this.graph.gridSize = 3;
      this.graph.setBorder(160);

      // 开启方块上的文字编辑功能
      this.graph.setCellsEditable(false);
      // 禁止双击修改内容(弃用)
      // this.graph.dblClick = (evt, cell) => {
      //   var model = this.graph.getModel();
      //   if (model.isVertex(cell)) {
      //     return false;
      //   }
      // };
      // Disables synchronous loading of resources
      // 可用于禁用HTML的泳道标签，避免冲突(返回false即可)
      // 判断是否为泳道标签
      // this.graph.isHtmlLabel = function (cell) {
      //   return this.isSwimlane(cell);
      // };
      //准备撤销还原功能
      // 构造具有给定历史记录大小的新撤消管理器。默认100步
      this.undoMng = new mxUndoManager();
      let listener = (sender, evt) => {
        this.undoMng.undoableEditHappened(evt.getProperty('edit'));

      };
      this.graph.getModel().addListener(mxEvent.UNDO, listener);
      this.graph.getView().addListener(mxEvent.UNDO, listener);
      // 创建缩略图
      this.outline = new mxOutline(this.graph, this.$refs.styleSelect.$refs.showMap);
      if ((this.graph) == null || (this.graph) == undefined) {
        return;
      }
      // 从value中获取显示的内容(如果节点的value为空则显示节点的title)
      this.graph.convertValueToString = (cell) => {
        return cell['value'] ? cell['value'] : cell['title'];
      };

      let that = this
      // 添加单击事件监听器
      this.graph.addListener(mxEvent.CLICK, function (sender, evt) {
          var cell = evt.getProperty('cell'); // 被点击的单元格（节点或边）

          if (cell !== null) { // 如果点击的是节点或边
              // 清除之前的定时器
              if (that.clickTimeout) {
                  clearTimeout(that.clickTimeout);
              }

              // 设置一个新的定时器
              that.clickTimeout = setTimeout(function () {
                  console.log('单击节点111:', cell);
                  if(cell.style === undefined){
                    return
                  }
                  if (!that.currObjMx.fid && !that.currObjMx.fId) {
                    return
                  }
                  console.log('这是单击')
                  if (!that.isConnection){
                    that.$emit('update:isMxgraphVue2', true)
                  }
                  
                  that.isConnection = false


                  // 在这里添加单击节点时要执行的逻辑
              }, 300); // 延迟时间可以根据需要调整
          }
          evt.consume(); // 防止默认行为
      });


      this.graph.addListener(mxEvent.DOUBLE_CLICK, function (sender, evt) {
          var cell = evt.getProperty('cell'); // 被双击的单元格（节点或边）
          if (cell !== null) { // 如果双击的是节点或边
              // 清除定时器以防止单击逻辑被执行
              if (that.clickTimeout) {
                  clearTimeout(that.clickTimeout);
                  that.clickTimeout = null;
              }
              if(cell.style === undefined){
                return
              }
              if (!that.isConnection) {
                if (!that.recipeId) {
                  that.$message.error('请选择配方');
                  return
                }
                // that.$emit('isShowDialogJobFn',that.selectedNode.currObjMx || {})
                that.$emit('update:isShowDialogJob',true)

              }
              that.isConnection = false
              console.log('双击节点:', cell.value);
              // 在这里添加双击节点时要执行的逻辑，比如打开编辑模式
          }
          evt.consume(); // 防止默认行为
      });


    },
    // 布局
    graphLayout(animate, layoutType) {
      this.graph.getModel().beginUpdate()
      try {
        if (layoutType === 'randomLayout') {
          // 随机布局
          mxFastOrganicLayout.prototype.minDistanceLimit = 100
          // eslint-disable-next-line new-cap
          var layout = new mxFastOrganicLayout(this.graph)
          layout.forceConstant = 500
          layout.execute(this.graph.getDefaultParent())
        } else if (layoutType === 'hierarchicalLayout') {
          // 分层布局
          mxHierarchicalLayout.prototype.intraCellSpacing = 300
          mxHierarchicalLayout.prototype.fineTuning = false
          mxHierarchicalLayout.prototype.traverseAncestors = false
          mxHierarchicalLayout.prototype.resizeParent = true
          // 无关系实体之间的间距
          mxHierarchicalLayout.prototype.interHierarchySpacing = 200
          // 层级之间的距离
          mxHierarchicalLayout.prototype.interRankCellSpacing = 800

          // eslint-disable-next-line new-cap
          var hierarchicallayout = new mxHierarchicalLayout(this.graph, mxConstants.DIRECTION_NORTH)
          hierarchicallayout.execute(this.graph.getDefaultParent())
        } else if (layoutType === 'compactTreeLayout') {
          // 树形布局
          // eslint-disable-next-line new-cap
          var compactTreelayout = new mxCompactTreeLayout(this.graph)
          compactTreelayout.execute(this.graph.getDefaultParent())
        } else if (layoutType === 'circleLayout') {
          // 圆形布局
          // eslint-disable-next-line new-cap
          var circleLayout = new mxCircleLayout(this.graph, 400)
          circleLayout.execute(this.graph.getDefaultParent())
        }
      } finally {
        // 是否开启布局动画
        if (animate) {
          // eslint-disable-next-line new-cap
          var morph = new mxMorphing(this.graph, 20, 7.7, 40)
          morph.addListener(mxEvent.DONE, () => {
            this.graph.getModel().endUpdate()
          })
          morph.startAnimation()
        } else {
          this.graph.getModel().endUpdate()
        }
      }
    },
    // 初始化基础节点
    initGeneralTool() {
      var generalToolbarDomArray = this.$refs.generalToolItems;
      // 判断是否为数组且数组是否为空
      if (!(generalToolbarDomArray instanceof Array || generalToolbarDomArray.length <= 0)) {
        return;
      }

      generalToolbarDomArray.forEach((dom, domIndex) => {
        var toolItem = this.generalToolbarItems[domIndex];
        var { width, height } = toolItem;
        var itemClass = (toolItem.class);
        
        //新增基础节点
        var generalDropHandler = (graph, evt, dropCell, x, y) => {
          
          // 先选择配方 才能拖拽图标
          if(this.$props.trainId === ''){
            return this.$message.warning("请先选择配方");
          }
          // 新增开始结束只能有一个
          if(toolItem.id === "startCircle" || toolItem.id === "endCircle"){
            const nodes = this.jsonData?.cells?.nodes || [];
            console.log(nodes,'sdsfds')
            const startCircle = nodes.filter(key => key.id == 2);
            const endCircle = nodes.filter(key => key.id == 3);
            if(toolItem.id === "startCircle" && startCircle.length){
              return this.$message.warning("开始节点只能有一个");
            }
            if(toolItem.id === "endCircle" && endCircle.length){
              return this.$message.warning("结束节点只能有一个");
            }
          }else{
            const nodes = this.jsonData?.cells?.nodes || [];
            const startCircle = nodes.filter(key => key.id == 2);
            if(startCircle.length == 0){
              return this.$message.warning("请先添加开始节点");
            }
          }
   
          const drop = !R.isNil(dropCell);
          console.log(x, y, 'drop111')
          // drop && this.$message.info(`${toolItem['title']}节点进入${dropCell.title}`);
          const realX = drop ? x - dropCell.geometry.x : x;
          const realY = drop ? y - dropCell.geometry.y : y;
          const { width, height } = toolItem;
          console.log(realX, realY, 'drop')
          const styleObj = toolItem.style;
          const style = Object.keys(styleObj).map((attr) => `${attr}=${styleObj[attr]}`).join(';');
          const parent = drop ? dropCell : this.graph.getDefaultParent();
          console.log(parent, "parent")
          console.log(style, "style")
          this.graph.getModel().beginUpdate();
          try {
            this.idSeed ++
            console.log(1113)
            let vertex = this.graph.insertVertex(parent, null, null, realX - (width / 2), realY - (height / 2), width, height, style + ';whiteSpace=wrap;word-break=break-all;endArrow=none;resizable=0');
            //vertex.title = `<div style='word-break:break-all'>` + toolItem['title'] + '</div>';
            vertex.dropAble = toolItem['dropAble'];
            vertex.value = toolItem['title']
            vertex.itemType = toolItem['itemType']
            // vertex.id = toolItem['id'] + '-' + toolItem['idSeed'];
            // if(toolItem.id === "startCircle"){
            //   vertex.id = 2;
            // }else if(toolItem.id === "endCircle"){
            //   vertex.id = 3;
            // }else{
            //   vertex.id = this.idSeed;
            // }
            
            // toolItem['idSeed']++;
            vertex['isGroup'] = toolItem['id'].includes('group') ? true : false
            console.log(vertex,">>>>>>>>>",toolItem)
          } finally {
            console.log(22223)
            this.graph.getModel().endUpdate();
          }
        };
        // 设置节点被拖拽时的样式(预览)
        var generalcreateDragPreview = () => {
          var elt = document.createElement('div');
          elt.style.width = `${width}px`;
          elt.style.height = `${height}px`;
          elt.style.transform = 'translate(-50%,-50%)';
          elt.className = itemClass;
          return elt;
        };
        // 允许拖拽
        let ds = mxUtils.makeDraggable(dom, this.graph, generalDropHandler, generalcreateDragPreview(), 0, 0, true, true);
        ds.setGuidesEnabled(true);
      });
    },
    //获取最近的线，并吸附上去
    getNearByLine(cell) {
      const SNAP_THRESHOLD = 200;
      const edges = this.graph.getChildEdges(this.graph.getDefaultParent());
      console.log(edges, "edges")
      let minDistance = Infinity;
      let closestEdge = null;
      edges.forEach(edge => {
        const state = this.graph.view.getState(edge);
        const points = state.absolutePoints;
        console.log("points:", points)
        for (let i = 0; i < points.length - 1; i++) {
          const distance = this.calcDistanceToLine(cell.geometry, points[i], points[i + 1]);
          if (distance < minDistance) {
            minDistance = distance;
            closestEdge = edge;
          }
        }
      });
      if (minDistance <= SNAP_THRESHOLD && closestEdge) {
        console.log("closestEdge:", closestEdge)
        // 获取边的源节点和目标节点
        var source = this.graph.getModel().getTerminal(closestEdge, true);
        var target = this.graph.getModel().getTerminal(closestEdge, false);
        console.log("target:", target, "source:", source)
        // 删除原来的边
        this.graph.removeCells([closestEdge]);
        // 插入两条新的边
        this.graph.insertEdge(this.graph.getDefaultParent(), null, "", source, cell, 'endArrow=none');
        this.graph.insertEdge(this.graph.getDefaultParent(), null, "", cell, target, 'endArrow=none');
        var layout = new mxHierarchicalLayout(this.graph);
        layout.execute(this.graph.getDefaultParent());

      }

    },
    calcDistanceToLine(cellGeo, p1, p2) {
      const x = cellGeo.x + cellGeo.width / 2;
      const y = cellGeo.y + cellGeo.height / 2;
      // 向量法计算距离（公式实现略）
      return Math.abs((p2.y - p1.y) * x - (p2.x - p1.x) * y + p2.x * p1.y - p2.y * p1.x) /
        Math.sqrt((p2.y - p1.y) ** 2 + (p2.x - p1.x) ** 2);
    },
    // 初始化自定义图标
    initCustomToolbar() {
      // 获取工具栏中的自定义节点的dom
      var toolbarDomArray = this.$refs.toolItem;
      if (!(toolbarDomArray instanceof Array) || toolbarDomArray.length <= 0) {
        return;
      }

      toolbarDomArray.forEach((dom, domIndex) => {
        var toolItem = this.toolbarItems[domIndex];
        var { width, height } = toolItem;
        var image = toolItem.style.image;
        //定义拖拽后的回调函数
        var dropHandler = (graph, evt, cell, x, y) => {
          this.addCustomCell(cell, toolItem, x, y);
        };
        // 设置节点被拖拽时的样式(预览)
        var createDragPreview = () => {
          var elt = document.createElement('div');
          elt.style.border = '2px dotted black';
          elt.style.width = `${width}px`;
          elt.style.height = `${height}px`;
          elt.style.backgroundImage = `url(${image})`;
          elt.style.backgroundSize = `contain`;
          elt.style.transform = 'translate(-50%,-50%)';
          elt.style.backgroundRepeat = 'no-repeat';
          return elt;
        };
        // 允许拖拽
        let ds = mxUtils.makeDraggable(dom, this.graph, dropHandler, createDragPreview(), 0, 0, false, true, true);
        ds.setGuidesEnabled(true);
      });
    },
    // 拖拽组元素
    makeToolbarDraggable() {
      const grouptoolItem = this.$refs.grouptoolItem;
      if (!(grouptoolItem instanceof Array)) {
        return;
      }
      grouptoolItem.forEach((item, index) => {
        const toolItem = this.grouptoolItems[index];
        const { height, width } = toolItem;
        // 创建拖拽时的预览图形
        const createDragPreview = () => {
          const elt = document.createElement('div');
          elt.style.border = '2px dotted black';
          elt.style.width = `${width}px`;
          elt.style.height = `${height}px`;
          elt.style.transform = 'translate(-50%,-50%)';
          elt.className = toolItem.class;
          return elt;
        };
        // drop的处理函数
        const dropHandler = (graph, evt, cell, x, y) => {
          this.instanceCell(cell, toolItem, x, y);
        };
        // 获取拖放的对象
        const getDropTarget = (graph, x, y) => {
          const cell = graph.getCellAt(x, y);
          return R.propOr(null, 'dropAble', cell) ? cell : null;
        };
        mxUtils.makeDraggable(item, this.graph, dropHandler, createDragPreview(index), 0, 0, false, true, true, getDropTarget);
      });
    },
    instanceCell(dropCell, toolItem, x, y) {
  console.log('开始创建节点');

  const drop = !R.isNil(dropCell);
  const realX = drop ? x - dropCell.geometry.x : x;
  const realY = drop ? y - dropCell.geometry.y : y;
  const { height, width } = toolItem;
  const parent = drop ? dropCell : this.graph.getDefaultParent();
  const tmpIndex = Date.now();

  this.graph.getModel().beginUpdate();
  try {
    // 如果是组合矩形，则创建三个部分并分组
    if (toolItem.id === 'combined-rectangle') {
      // 创建主矩形
      const mainRect = this.createMainRectangle(parent, realX, realY, width, height, tmpIndex);

      // 创建顶部的小矩形（statr）
      const statr = this.createCustomNode(parent, realX, realY-5, width, 20, tmpIndex, 'statr', mainRect);

      // 创建底部的小矩形（end）
      const end = this.createCustomNode(parent, realX, realY+80, width, 20, tmpIndex, 'end', mainRect);

      // 将这三个部分选中
      this.graph.setSelectionCells([mainRect, statr, end]);

      // 使用 groupCells 方法将这三个部分组合起来
      const group = this.graph.groupCells(null, 0); // 第二个参数是子单元格之间的间距

      // 设置组合样式（可选）
      if (group) {
        group.setStyle('fillColor=none;strokeColor=none');
      }

      // 最后选中这个组合
      this.graph.setSelectionCell(group);

      // 执行工具项的回调函数
      this.$nextTick(() => {
        const createdCallback = toolItem['created'];
        if (createdCallback instanceof Function) {
          createdCallback(this.graph, group, tmpIndex);
        }
      });
    } else {
      // 普通节点的创建逻辑
      const vertex = this.graph.insertVertex(
        parent,
        null,
        '',
        realX - (width / 2),
        realY - (height / 2),
        width,
        height,
        Object.keys(toolItem.style || {})
          .map((key) => `${key}=${toolItem.style[key]}`)
          .join(';')
      );

      vertex['title'] = toolItem['title'];
      vertex['dropAble'] = toolItem['dropAble'];
      vertex['id'] = toolItem['id'];
      vertex['isGroup'] = toolItem['id'].includes('group') ? true : false;

      // 执行工具项的回调函数
      this.$nextTick(() => {
        const createdCallback = toolItem['created'];
        if (createdCallback instanceof Function) {
          createdCallback(this.graph, vertex, tmpIndex);
        }
      });
    }
  } finally {
    this.graph.getModel().endUpdate();
  }
},

// 创建主矩形
createMainRectangle(parent, x, y, width, height, index) {
   const node =  this.graph.insertVertex(
    parent,
    null,
    `<div class="group-containersa group-containersa_${index}"><div class="embedded embedded_${index}" style="display:none"></div></div>`.replace(/[\r\n]/g, ''),
    x,
    y,
    width,
    height,
    'html=1;whiteSpace=wrap;word-break=break-all;strokeColor=none;fillColor=none;borderLeft=0;borderTop=2px solid black;borderRight=2px solid black;borderBottom=2px solid black;'
  );
  node.groupClass = 'group_container'
  node.createTime = index
  return node
},

  // 创建自定义小矩形（statr 或 end）
  createCustomNode(parent, x, y, width, height, index, type, mainRect) {
    const node = this.graph.insertVertex(
      parent,
      null,
      `<div class="${type} ${type}_${index}"></div>`.replace(/[\r\n]/g, ''),
      x,
      y,
      width,
      height,
      'html=1;whiteSpace=wrap;word-break=break-all;strokeColor=none;fillColor=none;'
    );
    node.branchType = type
    return node;
  },

    // 基础配置函数
    eventCenter() {

      // 给graph添加事件
      // 监听自定义事件
      this.graph.addListener(mxEvent.NORMAL_TYPE_CLICKED, (sender, evt) => {
        let cell = evt.properties.cell.state.cell;
        this.currentNormalType = cell;
      });
      // this.graph.addListener(mxEvent.VERTEX_START_MOVE, (sender, evt) => {
      //   console.log('VERTEX_START_MOVE', sender, evt);
      // });
      // 画布平移事件
      this.graph.addListener(mxEvent.PAN, (sender, evt) => {
        console.log('画布平移了', sender, evt);
      });
      // 新增节点事件
      this.graph.addListener(mxEvent.ADD_CELLS, (sender, evt) => {
        console.log(565656, evt)
        this.$nextTick(() => {
          let addCell = evt.properties.cells[0];
          if (addCell.vertex) {
            // 判断是否为组节点
            if (addCell.isGroup) {
              // this.$message.info('添加了一个组');
              let groupObj = _.pick(addCell, ['id', 'title', 'parent', 'geometry']);
              this.jsonData['cells']['groups'].push(groupObj);
            } else {
              let nodeObj = _.pick(addCell, ['id', 'title', 'parent', 'geometry']);
              console.log(nodeObj, 666)
              this.jsonData['cells']['nodes'].push(nodeObj);
              this.$message.info('添加了一个节点');
            }
            //  向jsonData中更新数据
          } else if (addCell.edge) {
            console.log(addCell)
            // addCell.id = this.idSeed ++
            let lineObj = _.pick(addCell, ['id', 'edge', 'source', 'parent', 'geometry', 'value']);
            this.jsonData['edges'].push(lineObj);
            console.log(lineObj,8978)
            this.isConnection = true //判断是在连线
            // this.$message.info('添加了一条线');
          }
          console.log(this.jsonData)
        })
      });

      //拖动节点的事件
      this.graph.addListener(mxEvent.CELLS_MOVED, (sender, evt) => {
        // console.log(this.graph, 'graph');
        let cellsName = [];
        this.$nextTick(() => {
          evt.properties.cells.forEach(item => {
            item.parent.id.includes('group') && cellsName.push(item.title);
          });
          evt.properties.cells[0].parent.id !== '1' && this.$message.info(`${[...cellsName]}节点进入${evt.properties.cells[0].parent.title}`);
        });
      });
      // 删除节点触发事件
      this.graph.addListener(mxEvent.CELLS_REMOVED, (sender, evt) => {
        this.$nextTick(() => {
          let removeCells = evt.properties.cells;
          console.log(removeCells, 'removeCells')
          removeCells.forEach(item => {
            // 拿每一个cellId在jsonData中进行遍历,并进行移除
            if (item.vertex) {
              // 判断是否为组节点
              if (item.isGroup) {
                // this.$message.info(`移除了${item.id}组`);
                this.jsonData['cells']['groups'].splice(this.jsonData['cells']['groups'].findIndex(jsonItem => { return jsonItem.id === item.id }), 1)
              } else {
                // this.$message.info(`移除${item.id}节点`);
                this.jsonData['cells']['nodes'].splice(this.jsonData['cells']['nodes'].findIndex(jsonItem => { return jsonItem.id === item.id }), 1)
              }
            } else if (item.edge) {
              // this.$message.info('移除了线');
              this.jsonData['edges'].splice(this.jsonData['edges'].findIndex(jsonItem => { return jsonItem.id === item.id }), 1)
            }
            // this.jsonData.forEach(item)
          });
        });
      });

    },
    // 配置鼠标事件
    configMouseEvent() {
      this.graph.addMouseListener(
        {
          currentState: null,
          previousStyle: null,

          mouseDown: (sender, evt) => {
            if (!evt.state) {
              console.log('点击了画布');
              return;
            } else if (evt.state.cell.edge) {
              console.log('点击了连线');
              return;
            }

            const cell = evt.state.cell;
            if (cell) {
              this.selectedNode = cell; // 记录当前选中的节点
              this.$emit('handleSelectedNode',this.currObjMx || {})
              console.log("选中的节点:", evt);
            }


            let clickNormalType = false;
            if (cell.style !== undefined) {
              clickNormalType = cell.style.includes('normalType');
            }
            if (clickNormalType) {
              // 使用 mxGraph 事件中心，注册自定义事件
              this.graph.fireEvent(new mxEventObject(mxEvent.NORMAL_TYPE_CLICKED, 'cell', evt));
            } else {
              return;
            }
          },

          mouseMove: (sender, me) => {
            // console.log('mouseMove')
            this.graphX = Math.ceil(me.graphX);
            this.graphY = Math.ceil(me.graphY);
          },

          mouseUp: (sender, evt) => {
            console.log('mouseUp', evt);
            if (evt.sourceState === undefined) {
              return false;
            } else {
              // evt.sourceState.cell.unitObj = this.currObjMx
              // evt.sourceState.cell.value = this.currObjMx.jobDesc
              var cell = evt.sourceState.cell;
              console.log(this.graph.getModel().isEdge(cell), 8889)

              // console.log(cell);
              //  var cell = this.graph.getSelectionCell();
              if (cell) {
                this.textValue = cell['value'] ? cell['value'] : cell['title'];
                console.log(this.textValue, 'textValue')
                console.log('cellValuie', cell)
                cell.vertex ? this.isNode = true : this.isNode = false;
                var getcellStyle = cell.getStyle() ? cell.getStyle() : null;
                if (!this.isNode) {
                  // 点击的不是节点
                  getcellStyle ? this.edgeStyle = getcellStyle : 'orthogonalEdgeStyle';
                } else {
                  // 点击的是节点
                  // console.log('getcellStyle', getcellStyle);
                  if (getcellStyle) {
                    var arr = getcellStyle.split(';');
                    // arr.pop()
                    var styleObject = {};
                    arr.forEach(item => {
                      styleObject[item.split('=')[0]] = item.split('=')[1];
                    });
                    this.cellStyle = styleObject;
                  }
                }
              } else {
                this.$message.error('请选择节点或者连线');
              }
            }
          },
          mouseDoubleClick: (sender, evt) => {
            console.log(1112)
          }
        });
    },
    //配置键盘事件
    configKeyEvent() {
      // 启动盘事件键
      this.keyHandler = new mxKeyHandler(this.graph);
      console.log(this.keyHandler, 9999)
      // 键盘按下删除键绑定事件
      this.keyHandler.bindKey(46, () => {
        // console.log('sss')
        this.deleteNode()
      });
      this.keyHandler.bindControlKey(65, () => {
        this.graph.selectAll();
      });
      this.keyHandler.bindControlKey(67, () => {
        this.copy();
      });
      this.keyHandler.bindControlKey(88, () => {
        this.cut();
      });
      this.keyHandler.bindControlKey(86, () => {
        this.paste();
      });
      this.keyHandler.bindControlKey(89, () => {
        this.goForward();
      });
      this.keyHandler.bindControlKey(90, () => {
        this.goBack();
      });

    },
    handleKeyDown(event) {
      console.log(event)
      if (event.ctrlKey && event.key === 'z') {
        // Ctrl + Z 按下的逻辑处理
        this.goBack();
      } else if (event.ctrlKey && event.key === 'y') {
        this.goForward();
      }
    },
    //配置右键菜单栏
    configMenu() {
  // 禁用浏览器默认的右键菜单栏
  mxEvent.disableContextMenu(this.$refs.container);

  this.graph.popupMenuHandler.factoryMethod = (menu, cell) => {
    if (!cell) return;

    // 只有 branchType 为 end 的节点才会显示菜单
    if (cell.branchType !== 'end') return;

    menu.addItem('设置并行', null, () => {
      const model = this.graph.getModel();
      model.beginUpdate();
      try {
        if (cell.branchType === 'end') {
          // 设置当前节点 itemType=24
          cell.itemType = 24;
          console.log(`已为节点 ${cell.id} 设置 itemType=24`);

          // 获取当前节点的父级单元格
          const parentCell = model.getParent(cell);
          const children = model.getChildren(parentCell);

          if (children) {
            children.forEach(child => {
              // 如果子节点的 branchType 是 statr，则添加 itemType=21
              if (child.branchType === 'statr') {
                child.itemType = 21;
              }

              // 如果是主矩形，将上下两条边恢复为单线条
              if (child.groupClass === 'group_container') {
                const embeddedDiv = document.querySelector(`.embedded_${child.createTime}`);
                if (embeddedDiv) {
                  embeddedDiv.style.display = 'none';
                }
                console.log(`已隐藏主矩形 ${child.createTime} 内的 .embedded`);
              }
            });
          }
        }
        // 刷新画布以反映更改
        this.graph.refresh();
      } finally {
        model.endUpdate();
      }
    });

    menu.addItem('设置串行', null, () => {
      const model = this.graph.getModel();
      model.beginUpdate();
      try {
        if (cell.branchType === 'end') {
          // 设置当前节点 itemType=22
          cell.itemType = 22;
          console.log(`已为节点 ${cell.id} 设置 itemType=22`);

          // 获取当前节点的父级单元格
          const parentCell = model.getParent(cell);
          const children = model.getChildren(parentCell);
          console.log(children,4545)
          if (children) {
            children.forEach(child => {
              // 如果子节点的 branchType 是 statr，则添加 itemType=21
              if (child.branchType === 'statr') {
                child.itemType = 21;
              }
              // 如果是主矩形，将上下两条边变为双线条
              if (child.groupClass === 'group_container') {
                const embeddedDiv = document.querySelector(`.embedded_${child.createTime}`);
                if (embeddedDiv) {
                  embeddedDiv.style.display = 'block';
                }
                console.log(embeddedDiv)

                console.log(`已显示主矩形 ${child.id} 内的 .embedded`);
              }
            });
          }
        }
        // 刷新画布以反映更改
        this.graph.refresh();
      } finally {
        model.endUpdate();
      }
    });

    menu.addSeparator();
  };
},
    //  配置全局样式
    configureStylesheet(graph) {
      // 设置节点的文字可被移动
      graph.vertexLabelsMovable = false;
      // 设置鼠标悬浮至节点或者连线时高亮显示的颜色
      new mxCellTracker(graph, '#409eff');
      console.log(graph, 87878)
      var style = new Object();
      style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_LABEL;
      style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
      style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE;
      style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER;
      style[mxConstants.STYLE_IMAGE_ALIGN] = mxConstants.ALIGN_CENTER;
      style[mxConstants.STYLE_IMAGE_VERTICAL_ALIGN] = mxConstants.ALIGN_CENTER;
      // style[mxConstants.STYLE_SPACING_TOP] = 6;
      style[mxConstants.STYLE_SPACING_LEFT] = 5;
      // style[mxConstants.STYLE_GRADIENTCOLOR] = 'skyblue'; // 渐变颜色
      style[mxConstants.STYLE_STROKECOLOR] = '#333333'  //'#5d65df';   // 线条颜色
      style[mxConstants.STYLE_FILLCOLOR] = '#FFFFFF';
      style[mxConstants.STYLE_FONTCOLOR] = '#1d258f';     // 字体颜色
      style[mxConstants.STYLE_FONTFAMILY] = 'Verdana';    // 字体风格
      style[mxConstants.STYLE_FONTSIZE] = '12';           // 字体大小
      style[mxConstants.STYLE_FONTSTYLE] = '0';           // 斜体字
      style[mxConstants.WORD_WRAP] = 'normal';             // 文字换行    word-break: break-all;
      style[mxConstants['word-break']] = 'break-all';             // 文字换行
      style[mxConstants.STYLE_WHITE_SPACE] = 'wrap';             // 文字换行
      style[mxConstants.STYLE_ROUNDED] = false;             // 圆角
      style[mxConstants.STYLE_IMAGE_WIDTH] = '28';        // 图片宽度
      style[mxConstants.STYLE_IMAGE_HEIGHT] = '28';       // 图片高度
      style[mxConstants.STYLE_OPACITY] = '100';            // 节点透明度(不包含字体)
      graph.getStylesheet().putDefaultVertexStyle(style);

      style = new Object();
      style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_SWIMLANE;
      style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
      style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER;
      style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_TOP;
      style[mxConstants.STYLE_FILLCOLOR] = '#409eff';
      // style[mxConstants.STYLE_GRADIENTCOLOR] = '#409eff';
      style[mxConstants.STYLE_STROKECOLOR] = '#409eff';
      style[mxConstants.STYLE_FONTCOLOR] = '#000000';
      style[mxConstants.STYLE_ROUNDED] = false;
      style[mxConstants.STYLE_OPACITY] = '80';
      style[mxConstants.STYLE_STARTSIZE] = '30';
      style[mxConstants.STYLE_FONTSIZE] = '16';
      style[mxConstants.STYLE_FONTSTYLE] = 1;
      graph.getStylesheet().putCellStyle('group', style);

      style = new Object();
      style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_IMAGE;
      style[mxConstants.STYLE_FONTCOLOR] = '#774400';
      style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
      style[mxConstants.STYLE_PERIMETER_SPACING] = '6';
      style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_LEFT;
      style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE;
      style[mxConstants.STYLE_FONTSIZE] = '10';
      style[mxConstants.STYLE_FONTSTYLE] = 2;
      style[mxConstants.STYLE_IMAGE_WIDTH] = '16';
      style[mxConstants.STYLE_IMAGE_HEIGHT] = '16';
      style[mxConstants.STYLE_BACKGROUNDCOLOR] = 'transparent';
      graph.getStylesheet().putCellStyle('port', style);


      style = graph.getStylesheet().getDefaultEdgeStyle();
      style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = '#FFFFFF';
      style[mxConstants.STYLE_STROKEWIDTH] = '2';
      style[mxConstants.STYLE_ROUNDED] = true;
      // 获取全局Edge、label样式
      var edgeStyle = this.graph.getStylesheet().getDefaultEdgeStyle();
      let labelStyle = this.graph.getStylesheet().getDefaultVertexStyle();
      // labelStyle[mxConstants.STYLE_WHITE_SPACE] = 'wrap'; //自动换行
      console.log(labelStyle, 'labelStyle')
      // 设置连线风格(设置为正交折线)
      edgeStyle['edgeStyle'] = 'orthogonalEdgeStyle';





      // 选中 cell/edge 后的伸缩大小的点/拖动连线位置的点的颜色
      // style[mxConstants.STYLE_WHITE_SPACE] = 'wrap'

      mxConstants.HANDLE_FILLCOLOR = '#409eff';
      mxConstants.HANDLE_STROKECOLOR = 'transparent';
      mxConstants.STYLE_ANCHOR_POINT_DIRECTION = 'anchorPointDirection';
      mxConstants.STYLE_STYLE_ROTATION = 'rotation';
      // 是否缩放网格
      mxGraphHandler.prototype.scaleGrid = true;
      mxGraph.prototype.pageBreakDashed = false;

      // 指定是否应使用其他单元格对齐当前所选内容的右侧，中间或左侧。默认为false。
      mxGraphHandler.prototype.guidesEnabled = true;
      mxGraphHandler.prototype.htmlPreview = false;
      mxGraphHandler.prototype.allowLivePreview = true;
      // 指定预览形状的颜色。默认为黑色。
      mxGraphHandler.prototype.previewColor = "red"
      // 应该使用实时预览的最大单元数。默认值为0，表示没有实时预览。
      mxGraphHandler.prototype.maxLivePreview = 100;

      // Alt 按下禁用导航线
      mxGraphHandler.prototype.useGuidesForEvent = function (me) {
        return !mxEvent.isAltDown(me.getEvent());
      };
      // 导航线颜色
      mxConstants.GUIDE_COLOR = '#1a73e8';
      // 导航线宽度
      mxConstants.GUIDE_STROKEWIDTH = 2;
      // 导航线自动连接到目标
      mxEdgeHandler.prototype.snapToTerminals = true;
      // 选中线条时的虚线颜色
      mxConstants.EDGE_SELECTION_COLOR = '#99ccff';
      // mxConstants.DEFAULT_INVALID_COLOR = 'yellow';
      // mxConstants.INVALID_CONNECT_TARGET_COLOR = 'yellow';
      // 连线(未满足连线要求)时预览的颜色
      mxConstants.INVALID_COLOR = '#99ccff';
      // 连线(满足连线要求)时预览的颜色
      mxConstants.VALID_COLOR = 'blue';
      // mxConstants.GUIDE_COLOR = 'yellow';
      // mxConstants.LOCKED_HANDLE_FILLCOLOR = '#24bcab';
      // 选中节点时选中框的颜色
      mxConstants.VERTEX_SELECTION_COLOR = '#99ccff';

      //折叠-/展开+图标大小
      // mxGraph.prototype.collapsedImage = new mxImage('images/collapsed.gif', 15, 15);
      // mxGraph.prototype.expandedImage = new mxImage('images/expanded.gif', 15, 15);

      // 配置节点中心的连接图标(注釋掉即可指定錨點連接到另一個節點的錨點上)
      mxConnectionHandler.prototype.connectImage = new mxImage('./icon/connectionpoint.png', 14, 14);
      // 显示中心端口图标
      graph.connectionHandler.targetConnectImage = false;
      // 是否开启浮动自动连接
      this.graph.connectionHandler.isConnectableCell = function () {
        return true;
      };
      // 设定锚点的位置、可编辑状态和图标
      mxConstraintHandler.prototype.pointImage = new mxImage('icon/dot.svg', 10, 10)
      // 设置锚点上的高亮颜色
      mxConstraintHandler.prototype.createHighlightShape = function () {
        return new mxEllipse(null, '#409eff99', '#409eff99', 15)
      }

      mxShape.prototype.constraints = [
        new mxConnectionConstraint(new mxPoint(0, 0), true),
        new mxConnectionConstraint(new mxPoint(0.25, 0), true),
        new mxConnectionConstraint(new mxPoint(0.5, 0), true),
        new mxConnectionConstraint(new mxPoint(0.75, 0), true),
        new mxConnectionConstraint(new mxPoint(0, 0.25), true),
        new mxConnectionConstraint(new mxPoint(0, 0.5), true),
        new mxConnectionConstraint(new mxPoint(0, 0.75), true),
        new mxConnectionConstraint(new mxPoint(1, 0), true),
        new mxConnectionConstraint(new mxPoint(1, 0.25), true),
        new mxConnectionConstraint(new mxPoint(1, 0.5), true),
        new mxConnectionConstraint(new mxPoint(1, 0.75), true),
        new mxConnectionConstraint(new mxPoint(0, 1), true),
        new mxConnectionConstraint(new mxPoint(0.25, 1), true),
        new mxConnectionConstraint(new mxPoint(0.5, 1), true),
        new mxConnectionConstraint(new mxPoint(0.75, 1), true),
        new mxConnectionConstraint(new mxPoint(1, 1), true)];
      mxPolyline.prototype.constraints = null;
    },

    //设置连线样式
    changeDashed(value) {
      var cell = this.graph.getSelectionCells();
      this.graph.setCellStyles(mxConstants.STYLE_DASHED, value, [...cell]);
      // this.graph.refresh(cell)
    },

    //设置线条颜色样式
    changeStrokeColor(value) {
      var cell = this.graph.getSelectionCells();
      this.graph.setCellStyles(mxConstants.STYLE_STROKECOLOR, value, [...cell]);
      // this.graph.refresh(cell)
    },

    //设置线条宽度
    changeStrokeWidth(value) {
      var cell = this.graph.getSelectionCells();
      this.graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, [...cell]);
      // this.graph.refresh(cell)
    },

    //设置字体大小
    changeFontSize(value) {
      var cell = this.graph.getSelectionCells();
      this.graph.setCellStyles(mxConstants.STYLE_FONTSIZE, value, [...cell]);
      // this.graph.refresh(cell)
    },

    //设置字体颜色
    changeFontColor(value) {
      var cell = this.graph.getSelectionCells();
      this.graph.setCellStyles(mxConstants.STYLE_FONTCOLOR, value, [...cell]);
      // this.graph.refresh(cell)
    },

    //设置线条说明的背景颜色
    changeLabelBackgroundColor(value) {
      var cell = this.graph.getSelectionCells();
      this.graph.setCellStyles(mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, value, [...cell]);
      // this.graph.refresh(cell)
    },

    changeFillColor(value) {
      var cell = this.graph.getSelectionCells();
      this.graph.setCellStyles(mxConstants.STYLE_FILLCOLOR, value, [...cell]);
    },

    changeShadow(value) {
      var cell = this.graph.getSelectionCells();
      this.graph.setCellStyles(mxConstants.STYLE_SHADOW, +(value), [...cell]);
    },

    changeFontStyle(value) {
      var cell = this.graph.getSelectionCells();
      this.graph.setCellStyles(mxConstants.STYLE_FONTSTYLE, value, [...cell]);
    },

    changeNodeimage(value) {
      var cell = this.graph.getSelectionCells();
      this.graph.setCellStyles(mxConstants.STYLE_IMAGE, value, [...cell]);
    },

    // 删除节点
    deleteNode() {
      var cells = this.graph.getSelectionCells();
      this.graph.removeCells([...cells]);
    },

    // 修改连线样式
    edgeChange(value) {
      try {
        var cell = this.graph.getSelectionCells();
        this.graph.setCellStyles('edgeStyle', value, [...cell]);
        let style = cell[0].style;
        let valStr = cell[0].value;
        this.graph.removeCells(cell);
        let parent = this.graph.getDefaultParent();
        let v1 = '';
        let v2 = '';
        // 获取ID单元
        parent['children'].forEach(item => {
          item['id'] === cell[0].source.id ? v1 = item : false;
          item['id'] === cell[0].target.id ? v2 = item : false;
        });
        this.graph.getModel().beginUpdate();
        this.graph.insertEdge(parent, null, valStr, v1, v2, style);
        this.graph.getModel().endUpdate();
        this.$message.success('切换连线样式成功');
      } catch (error) {
        this.$message.error('切换连线样式失败');
        console.log(error);
      }
    },

    // 修改节点文本内容
    textValueChange(value) {
      var cell = this.graph.getSelectionCells();
      console.log(value, '节点文本新内容', this.graph);
      this.graph.cellLabelChanged(cell[0], value)
    },
    changeConfigOrder(val) {
      // 获取当前的normalType元素,并更新他的title
      this.currentNormalType.title = val.newConfigOrder;
      // 修改指定cell的背景图片
      this.graph.setCellStyles(mxConstants.STYLE_IMAGE, `./images/order/unselect-${val.newConfigOrder}.png`, [this.currentNormalType]);
      this.graph.refresh(this.currentNormalType);
    },

    //复制
    copy() {
      let selectionCells = this.graph.getSelectionCells();
      mxClipboard.copy(this.graph, selectionCells);
    },
    //粘贴
    paste() {
      mxClipboard.paste(this.graph);
    },
    //剪切
    cut() {
      var cells = [];
      cells = this.graph.getSelectionCells();
      mxClipboard.cut(this.graph, cells);
    },
    // 前进
    goForward() {
      this.undoMng.redo();
    },

    // 撤退
    goBack() {
      this.undoMng.undo();
    },

    // 放大
    zoomIn() {
      this.graph.zoomIn();
    },

    // 缩小
    zoomOut() {
      this.graph.zoomOut();
    },

    // 等比例缩放
    autoSize() {
      // 方法一
      // this.editor.execute('actualSize');
      //方法二：
      this.graph.zoomActual();
      this.graph.fit();//自适应
      this.graph.center();//将画布放到容器中间
    },

    // 生成图片
    showImage() {
      this.editor.execute('show');//直接页面跳转,并以svg流程图
      // 下载svg流程图
      console.log('this.gtaph', this.graph)
      const svg = this.exportModelSvg();
      const blob = new Blob([svg], { type: 'image/svg+xml' });
      const url = URL.createObjectURL(blob);
      let link = document.createElement('a');
      link.href = url;
      link.download = 'model.svg';
      link.click();
    },

    exportModelSvg() {
      let scale = this.graph.view.scale;
      let bounds = this.graph.getGraphBounds();
      let border = 10;

      // Prepares SVG document that holds the output
      let svgDoc = mxUtils.createXmlDocument();
      let root = (svgDoc.createElementNS != null) ?
        svgDoc.createElementNS(mxConstants.NS_SVG, 'svg') : svgDoc.createElement('svg');

      if (root.style != null) {
        root.style.backgroundColor = '#FFFFFF';
      } else {
        root.setAttribute('style', 'background-color:#FFFFFF');
      }

      if (svgDoc.createElementNS == null) {
        root.setAttribute('xmlns', mxConstants.NS_SVG);
      }
      let width = Math.ceil(bounds.width * scale / scale + 2 * border);
      let height = Math.ceil(bounds.height * scale / scale + 2 * border);
      root.setAttribute('class', 'svg-container');
      root.setAttribute('width', width + 'px');
      root.setAttribute('height', height + 'px');
      root.setAttribute('viewBox', "0 0 " + width + " " + height);
      root.setAttribute('xmlns:xlink', mxConstants.NS_XLINK);
      root.setAttribute('version', '1.1');

      // Adds group for anti-aliasing via transform
      let group = (svgDoc.createElementNS != null) ?
        svgDoc.createElementNS(mxConstants.NS_SVG, 'g') : svgDoc.createElement('g');
      group.setAttribute('transform', 'translate(0.5,0.5)');
      root.appendChild(group);
      svgDoc.appendChild(root);

      // Renders graph. Offset will be multiplied with state's scale when painting state.
      let svgCanvas = new mxSvgCanvas2D(group);
      svgCanvas.translate(Math.floor(border / scale - bounds.x), Math.floor(border / scale - bounds.y));
      svgCanvas.scale(scale);

      let imgExport = new mxImageExport();
      imgExport.drawState(this.graph.getView().getState(this.graph.model.root), svgCanvas);

      //let xml = encodeURIComponent(mxUtils.getXml(root)); //no need
      let xml = mxUtils.getXml(root);
      return xml;

    },
    // 开始导入xml文件
    inPutXml() {
      this.isOutputXml = false;
      this.uploadDataVisible = true;
      this.graphXml = '';
    },

    // 导入xml文件后更新视图
    uploadPaintFlow(newvalue) {
      this.graph.selectAll();
      this.graph.removeCells(this.graph.getSelectionCells());
      setTimeout(() => {
        this.decode(newvalue, this.graph);
        this.$message.success('渲染成功');
      }, 1000);
    },

    createXmlDom(str) {
      if (document.all) { //判断浏览器是否是IE
        var xmlDom = new ActiveXObject('Microsoft.XMLDOM');
        xmlDom.loadXML(str);
        return xmlDom;
      } else {
        return (new DOMParser()).parseFromString(str, 'text/xml');
      }
    },

    // 渲染xml流程图
    decode(graphXml) {
      this.graph.getModel().beginUpdate();
      try {
        var xmlDoc = this.createXmlDom(graphXml);
        var node = xmlDoc.documentElement;
        console.log(node,"nodenodenode")
        var dec = new mxCodec(node.ownerDocument);
        
        dec.decode(node, this.graph.getModel());
      } finally {
        this.graph.getModel().endUpdate();
        console.log('this.$Notice.destroy()',"-------------")
        setTimeout(() => {
          this.graph.refresh(); // 强制刷新视图
          console.log('渲染完成');
        }, 1000); // 延迟 100ms
        // 渲染完成调整位置
        // this.autoSize()
      }
    },
    // 导出xml文件
    outPutXml() {
      this.isOutputXml = true;
      this.uploadDataVisible = true;
      this.graphXml = this.encode(this.graph);
    },

    // 导出xml数据
    encode(graph) {
      const encoder = new mxCodec();
      console.log(encoder)
      const result = encoder.encode(graph.getModel());
      console.log(result)
      return mxUtils.getPrettyXml(result);
    },


    // 加载案例流程图
    loadFlowCase(index) {
      this.$confirm('请确认您当前流程图数据已保存至本地 ?', '提示', {
        confirmButtonText: '我已保存',
        cancelButtonText: '取消加载',
        type: 'warning',
      }).then(() => {
        let loadData = '';
        switch (index) {
          case 1:
            loadData = xmlData1;
            break;
          case 2:
            loadData = xmlData2;
            break;
          case 3:
            loadData = xmlData3;
            break;
        }
        console.log('loadData', loadData);
        let newXml = mxUtils.load(loadData).request.response;
        this.graph.selectAll();
        this.graph.removeCells(this.graph.getSelectionCells());
        setTimeout(() => {
          this.decode(newXml, this.graph);

          this.$message.success('加载流程图案例成功');
        }, 1000);
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消加载流程图案例',
        });
      });
    },
    handleScroll(e) {
      // console.log(e)
      // if (e.wheelDelta >= 120) {
      //   this.graph.zoomIn()
      // } else {
      //   this.graph.zoomOut()
      // }
    },
    handleCloaeGraph() {
      this.isShowGraph = false
      this.$emit('update:widthVal', '60px')
    },
    handleOpenGraph() {
      this.isShowGraph = true
      this.$emit('update:widthVal', '100%')
    },
    handleSaveXml(){
      const xmlString = this.encode(this.graph);
      console.log(xmlString)
      const param = {
        equipClsId:"",
        equipId:"",
        graphContent:xmlString,
        recipeId:this.recipeId
      }
      formula.recipeGraphAdd(param).then(res => {
        console.log(res)
      })
    },
  },
  mounted() {
    // 检测浏览器兼容性
    if (!mxClient.isBrowserSupported()) {
      this.$message.error('当前浏览器不支持拓扑图功能，请更换浏览器访问，建议使用Chrome浏览器访问!');
    } else {
      // Overridden to define per-shape connection points
      mxGraph.prototype.getAllConnectionConstraints = function (terminal) {
        if (terminal != null && terminal.shape != null) {
          if (terminal.shape.stencil != null) {
            if (terminal.shape.stencil.constraints != null) {
              return terminal.shape.stencil.constraints;
            }
          } else if (terminal.shape.constraints != null) {
            return terminal.shape.constraints;
          }
        }
        return null;
      };
      this.createGraph();
      this.eventCenter();
      this.configMouseEvent();
      this.configMenu();
      this.$nextTick(() => {
        this.initCustomToolbar();
        this.initGeneralTool();
        this.makeToolbarDraggable();
        this.configKeyEvent();

      });
    }
    document.getElementById('graphContainer').addEventListener('mousewheel', this.handleScroll, true) // 监听（绑定）滚轮滚动事件
    window.removeEventListener('keydown', this.handleKeyDown);



    


  },
  destroyed() {
    document.getElementById('graphContainer').removeEventListener('mousewheel', this.handleScroll) //  离开页面清除（移除）滚轮滚动事件
    this.graph.destroy();
  },
}
</script>
<style>
@import "./general-shap.css";

.customToolbarContainer {
  width: 100%;
  height: calc(100vh - 160px) !important;
  display: flex;
  position: relative;
  overflow: hidden;
}

.customToolbarContainer .toolbarContainer {
  padding-top: 50px;
  font-size: 20px;
  background: #efefef;
  text-align: center;
  background-color: #fff;
  border-right: 1px solid #ededed;
  width: 60px;
  position: relative;
  box-sizing: border-box;
  overflow-y: scroll;
  padding-right: 10px;
}

.customToolbarContainer .toolbarContainer .general-toolbar .el-collapse-item__wrap .el-collapse-item__content {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
  align-content: space-around;
}

.customToolbarContainer .toolbarContainer .general-toolbar .el-collapse-item__wrap .el-collapse-item__content .common {
  width: 50px;
  cursor: pointer;
  height: 50px;
  white-space: wrap;
  text-align: center;
  position: relative;
}

.customToolbarContainer .toolbarContainer .general-toolbar .el-collapse-item__wrap .el-collapse-item__content .common .generalTooltitle {
  position: absolute;
  bottom: -10px;
  left: 0;
  width: 100%;
}

.customToolbarContainer .toolbarContainer .custom-toolbar .custom-node {
  display: inline-block;
  margin: 10px 0 0 0;
  width: 28%;
  height: 60px;
  border: 1px solid #000000;
  padding: 5px 0;
}

.customToolbarContainer .toolbarContainer .custom-toolbar .custom-node img {
  height: 34px;
}

.customToolbarContainer .toolbarContainer .custom-toolbar .rectangle-node {
  width: 45%;
  height: 40px;
  margin: 10px 0 0 0;
  background-color: #ffff;
  position: relative;
  border: 1px solid #000000;
  padding: none;
}

.customToolbarContainer .toolbarContainer .custom-toolbar .rectangle-node img {
  position: absolute;
  left: 0;
  height: 35px;
  top: 50%;
  transform: translateY(-50%);
}

.customToolbarContainer .toolbarContainer .custom-toolbar .rectangle-node .node-title {
  position: absolute;
  left: 60%;
  top: 50%;
  transform: translate(-50%, -50%);
}

.customToolbarContainer .toolbarContainer .custom-toolbar .el-collapse-item__content {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
  align-content: space-around;
}

.customToolbarContainer .toolbarContainer .custom-toolbar .el-collapse-item__content span {
  cursor: pointer;
}

.customToolbarContainer .toolbarContainer::-webkit-scrollbar {
  display: none;
}

.customToolbarContainer .graphContainer {
  margin-top: 50px;
  height: 100% !important;
  line-height: 100%;
  position: relative;
  overflow: hidden;
  background-color: #fff !important;
  flex: 1;
}

.customToolbarContainer .top-tools {
  position: absolute;
  display: flex;
  align-items: center;
  box-sizing: border-box;
  top: 0;
  left: 0;
  padding: 0 20px 0 0px;
  width: 100%;
  z-index: 1000;
  background-color: #fff;
  height: 50px;
  box-shadow: 0px 2px 8px -4px #c4c7c1;
}

.customToolbarContainer .top-tools .select-edgetype {
  width: 100px;
  margin-right: 10px;
}

.customToolbarContainer .mxRubberband {
  background-color: #3a3acf;
  position: absolute;
}

.customToolbarContainer .el-collapse-item__header {
  padding-left: 30px;
}

.customToolbarContainer .right-bar {
  width: 0 !important;
  background-color: #fff;
  height: 100%;
  position: absolute;
  right: 0;
  top: 0;
  border-left: 1px solid #ededed;
  padding-top: 50px;
  box-sizing: border-box;
}

.customToolbarContainer .right-bar .json-viewer {
  overflow: auto;
  position: absolute;
  top: 35%;
  width: 260px;
  height: 70%;
  bottom: 0;
  right: 0;
}

.customToolbarContainer .tools-group {
  display: flex;
  /* justify-content: center; */
}

.customToolbarContainer .tools-group button {
  margin-left: 22px;
}

.customToolbarContainer .tools-group button:first-child {
  margin-left: 0 !important;
}

.customToolbarContainer .aside-button-group {
  width: 100%;
  position: sticky;
  top: 0px;
  background: #ffffff;
  box-sizing: border-box;
  z-index: 1000;
  border: 1px solid #ededed;
  border-left: none;
}

table.mxPopupMenu {
  background: #fff;
  cursor: pointer;
  border: 1px solid #ededed;
  padding: 5px;
}

div.mxPopupMenu {
  position: absolute;
}

.shapgroud .el-collapse-item__content {
  display: flex;
  flex-wrap: wrap;
}

.shapgroud svg g path {
  fill: transparent;
}

.shapgroud svg g path {
  stroke: #515151;
  -webkit-text-fill-color: #515151;
}

.flow {
  stroke-dasharray: 8;
  animation: dash 0.5s linear;
  animation-iteration-count: infinite;
}

@keyframes dash {
  to {
    stroke-dashoffset: -16;
  }
}

.group-item {
  cursor: pointer;
  height: 40px;
  margin: 5px 0;
  line-height: 40px;
  width: 80%;
  display: inline-block;
  border: 1px solid #eeee;
}

.custom-rectangle-node {
  cursor: pointer;
  display: inline-block;
  width: 40px !important;
  height: 10px;
  background-color: #000;
  text-align: center;
  line-height: 20px;
}

.custom-rectangleFull-node {
  cursor: pointer;
  display: inline-block;
  width: 40px !important;
  height: 10px;
  background-color: #F0F0F0;
  text-align: center;
  line-height: 30px;
  border: 1px solid #000000;
  margin-top: 10px;
  box-shadow: #666 2px 2px 0px 0px
}
.custom-circle-start{
  width: 34px !important;
  height: 34px !important;
  border: 1px solid #000;
  border-radius: 50%;
  box-sizing: border-box;
  margin-bottom: 10px;
  margin-top: 10px;
}
.custom-circle-end{
  width: 34px !important;
  height: 34px !important;
  border: 1px solid #000;
  border-radius: 50%;
  box-sizing: border-box;
  margin-bottom: 10px;
  background: #EEEEEE;
}
.custom-rectangle-node1 {
  width: 50px;
  height: 30px;
  background: url("../../../../../../public/icon/xiankuang.svg") no-repeat;
  background-size: 100%;
  margin-top: 10px
}

.custom-rectangle-node2 {
  width: 40px !important;
  height: 20px;
  background: url("../../../../../../public/icon/tiaozhuan.svg") no-repeat;
  background-size: 100%;
}
.custom-rhombus-node {
  width: 100px; /* 设置宽度 */
  height: 0; /* 高度初始化为0 */
  border-bottom: 50px solid transparent; /* 底部边框透明 */
  border-left: 50px solid blue; /* 左侧边框为蓝色 */
  border-right: 50px solid transparent; /* 右侧边框透明 */
  position: relative; /* 相对定位，以便后续调整 */
}
 
.custom-rhombus-node{
  width: 30px;
  height:30px;
  border: 1px solid #333;
  margin-top: 10px;
  transform: rotate(45deg);
  margin-bottom: 20px;
}
.el-icon-s-unfold {
  font-size: 30px;
}
.group-containersa {
    width: 200px;
    height: 100px;
    border-left: 0 !important;
    border-top: 2px solid black;
    border-right: 2px solid black;
    border-bottom: 2px solid black;
    position: relative;
    overflow: visible;
    box-sizing: border-box;
}
.embedded{
  width: 200px;
  height: 90px;
  border-top: 2px solid black;
  border-bottom: 2px solid black;
  overflow: visible;
  margin-top: 3px;
  box-sizing: border-box;
  /* display: none; */
}
/* .statr{
    width: 200px;
    height: 25px;
    position: absolute;
    top: -12px;
}
.end{
    width: 200px;
    height: 25px;
    position: absolute;
    bottom: -12px;
} */
</style>

<style scoped>
.customToolbarContainer .graphContainer-background {
  background: url("../../../../../assets/images/bg.svg") !important;
  background-position: -1px -1px;
  border: none;
}


</style>